package br.ufal.tci.nexos.arcolive.tests;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Panel;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.util.Vector;

import javax.media.CannotRealizeException;
import javax.media.ControllerErrorEvent;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.Format;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.NoProcessorException;
import javax.media.Player;
import javax.media.Processor;
import javax.media.ProcessorModel;
import javax.media.RealizeCompleteEvent;
import javax.media.control.BufferControl;
import javax.media.format.UnsupportedFormatException;
import javax.media.format.VideoFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;
import javax.media.protocol.PushBufferDataSource;
import javax.media.protocol.PushBufferStream;
import javax.media.rtp.InvalidSessionAddressException;
import javax.media.rtp.Participant;
import javax.media.rtp.RTPManager;
import javax.media.rtp.ReceiveStream;
import javax.media.rtp.ReceiveStreamListener;
import javax.media.rtp.SendStream;
import javax.media.rtp.SessionAddress;
import javax.media.rtp.event.NewReceiveStreamEvent;
import javax.media.rtp.event.ReceiveStreamEvent;

import br.ufal.tci.nexos.arcolive.beans.ArCoLIVEPlayer;
import br.ufal.tci.nexos.arcolive.beans.RTPConnectorAdapter;

/**
 * CLASSNAME.java
 * 
 * CLASS DESCRIPTION
 * 
 * @see CLASSNAME
 * 
 * @author <a href="mailto:felipe@labpesquisas.tci.ufal.br">Felipe Barros Pontes</a>.
 * @author <a href="mailto:leandro@labpesquisas.tci.ufal.br">Leandro Melo de
 *         Sales</a>.
 * @since 0.1
 * @version 0.1
 * 
 * <p>
 * <b>Revisions:</b>
 * 
 * <p>
 * <b>yyyymmdd USERNAME:</b>
 * <ul>
 * <li> VERSION
 * </ul>
 */

public class ArCoLIVENetworkPlayerT extends ArCoLIVEPlayer implements
		Serializable, ReceiveStreamListener, ControllerListener {

	private RTPManager rtpTransmitManagers[];

	private RTPManager rtpReceiveManagers[];

	private DataSource receiveDataSource;
	
	Vector playerWindows = null;

	private int transmissionPort = 40000;
	
	private int receptionPort = 40020;

	private String targetHost = "192.168.7.55";

	private String videoFormatType;

	private String audioFormatType;

	private String sourcePath = "vfw://0";

	public ArCoLIVENetworkPlayerT() {
	}

	public void transmitStream() {
		int port;
		SendStream sendStream;
		SessionAddress localAddress, targetAddress;
		ContentDescriptor rtpContainer = new ContentDescriptor(
				ContentDescriptor.RAW_RTP);
		this.videoFormatType = VideoFormat.JPEG_RTP;
		//this.audioFormatType = AudioFormat.G723_RTP;
		VideoFormat videoFormat = new VideoFormat(this.videoFormatType);
		//AudioFormat audioFormat = new AudioFormat(this.audioFormatType);
		Format formats[] = { videoFormat/*, audioFormat*/ };
		ProcessorModel rtpProcessorModel = new ProcessorModel(new MediaLocator(
				this.sourcePath), formats, rtpContainer);

		Processor processor;
		try {
			processor = Manager.createRealizedProcessor(rtpProcessorModel);
			DataSource dataSource = processor.getDataOutput();
			PushBufferDataSource pbds = (PushBufferDataSource) dataSource;
			PushBufferStream pbss[] = pbds.getStreams();
			for (int i = 0; i < pbss.length; i++) {
				port = transmissionPort + 2 * i;
				rtpTransmitManagers = new RTPManager[pbss.length];
				rtpTransmitManagers[i] = RTPManager.newInstance();
				localAddress = new SessionAddress(InetAddress.getLocalHost(),
						port);
				rtpTransmitManagers[i].initialize(localAddress);
				targetAddress = new SessionAddress(InetAddress
						.getByName(targetHost), port);
				BufferControl bufferControl = (BufferControl) rtpTransmitManagers[i]
						.getControl("javax.media.control.BufferControl");
				if (bufferControl != null) {
					bufferControl.setBufferLength(400);
				}
				this.rtpTransmitManagers[i].addTarget(targetAddress);
				sendStream = rtpTransmitManagers[i].createSendStream(
						dataSource, i);
				sendStream.start();
				processor.start();
			}
		} catch (NoProcessorException e) {
			e.printStackTrace();
		} catch (CannotRealizeException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InvalidSessionAddressException e) {
			e.printStackTrace();
		} catch (UnsupportedFormatException e) {
			e.printStackTrace();
		}
	}

	public void handleStream(String[] sessions) {
		try {
			playerWindows = new Vector();
			this.rtpReceiveManagers = new RTPManager[sessions.length];
			for (int i = 0; i < sessions.length; i++) {
				this.rtpReceiveManagers[i] = (RTPManager) RTPManager
						.newInstance();
				this.rtpReceiveManagers[i].addReceiveStreamListener(this);
				rtpReceiveManagers[i].initialize(new RTPConnectorAdapter(
						InetAddress.getByName(this.targetHost), this.receptionPort));
				BufferControl bufferControl = (BufferControl) rtpReceiveManagers[i]
						.getControl("javax.media.control.BufferControl");
				if (bufferControl != null) {
					bufferControl.setBufferLength(400);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public DataSource getReceiveDataSource() {
		return this.receiveDataSource;
	}

	/*public void update(ReceiveStreamEvent receiveStreamEvent) {
		ReceiveStream stream;
		if (receiveStreamEvent instanceof NewReceiveStreamEvent) {
			stream = ((NewReceiveStreamEvent) receiveStreamEvent)
					.getReceiveStream();
			this.receiveDataSource = stream.getDataSource();
		}
	}*/
	
	public void update(ReceiveStreamEvent receiveStreamEvent) {
		RTPManager mgr = (RTPManager) receiveStreamEvent.getSource();
		Participant participant = receiveStreamEvent.getParticipant(); // could
		// be
		// null.
		ReceiveStream stream = receiveStreamEvent.getReceiveStream(); // could
		// be
		// null.

		// If new stream, create player for that stream and associate datasource
		if (receiveStreamEvent instanceof NewReceiveStreamEvent) {
			try {
				stream = ((NewReceiveStreamEvent) receiveStreamEvent)
						.getReceiveStream();
				DataSource dataSource = stream.getDataSource();
				Player player;
				player = javax.media.Manager.createPlayer(dataSource);
				player.addControllerListener(this);
				player.realize();
				PlayerWindow pw = new PlayerWindow(player, stream);
				playerWindows.addElement(pw);

				// Notify create() that a new stream has arrived
			} catch (Exception e) {
				System.out.println("NewReceiveStreamException "
						+ e.getMessage());
			}
		}

	}
	
	PlayerWindow find(Player p) {
		for (int i = 0; i < playerWindows.size(); i++) {
		    PlayerWindow pw = (PlayerWindow)playerWindows.elementAt(i);
		    if (pw.player == p)
			return pw;
		}
		return null;
	    }


	    PlayerWindow find(ReceiveStream strm) {
		for (int i = 0; i < playerWindows.size(); i++) {
		    PlayerWindow pw = (PlayerWindow)playerWindows.elementAt(i);
		    if (pw.stream == strm)
			return pw;
		}
		return null;
	    }
	    
	    public synchronized void controllerUpdate(ControllerEvent ce) {

	    	Player p = (Player)ce.getSourceController();

	    	if (p == null)
	    	    return;

	    	// Get this when the internal players are realized.
	    	if (ce instanceof RealizeCompleteEvent) {
	    	    PlayerWindow pw = find(p);
	    	    if (pw == null) {
	    		// Some strange happened.
	    		System.err.println("Internal error!");
	    		System.exit(-1);
	    	    }
	    	    pw.initialize();
	    	    pw.setVisible(true);
	    	    p.start();
	    	}

	    	if (ce instanceof ControllerErrorEvent) {
	    	    p.removeControllerListener(this);
	    	    PlayerWindow pw = find(p);
	    	    if (pw != null) {
	    		pw.close();	
	    		playerWindows.removeElement(pw);
	    	    }
	    	    System.err.println("AVReceive3 internal error: " + ce);
	    	}

	        }
	    
	    class PlayerWindow extends Frame {

	    	Player player;
	    	ReceiveStream stream;

	    	PlayerWindow(Player p, ReceiveStream strm) {
	    	    player = p;
	    	    stream = strm;
	    	}

	    	public void initialize() {
	    	    add(new PlayerPanel(player));
	    	}

	    	public void close() {
	    	    player.close();
	    	    setVisible(false);
	    	    dispose();
	    	}

	    	public void addNotify() {
	    	    super.addNotify();
	    	    pack();
	    	}
	        }


	        /**
	         * GUI classes for the Player.
	         */
	        class PlayerPanel extends Panel {

	    	Component vc, cc;

	    	PlayerPanel(Player p) {
	    	    setLayout(new BorderLayout());
	    	    if ((vc = p.getVisualComponent()) != null)
	    		add("Center", vc);
	    	    if ((cc = p.getControlPanelComponent()) != null)
	    		add("South", cc);
	    	}

	    	public Dimension getPreferredSize() {
	    	    int w = 0, h = 0;
	    	    if (vc != null) {
	    		Dimension size = vc.getPreferredSize();
	    		w = size.width;
	    		h = size.height;
	    	    }
	    	    if (cc != null) {
	    		Dimension size = cc.getPreferredSize();
	    		if (w == 0)
	    		    w = size.width;
	    		h += size.height;
	    	    }
	    	    if (w < 160)
	    		w = 160;
	    	    return new Dimension(w, h);
	    	}
	        }

	        public static void main(String[] args) {
				ArCoLIVENetworkPlayerT anp = new ArCoLIVENetworkPlayerT();
				anp.transmitStream();
				anp.handleStream(new String[]{"video"});
			}
	        
}